/* ----------------------------------------------------------------------------

	pax.fx.js Copyright (C) 2005, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */
/*
	Script: pax.fx
		This class allows various effects and graphical attribues of CSS classes to be manipulated, and multiple effects to be applied to one or more DOM elements;
		it utalises tweening, which allows good control over animations and effects.
		
	Note: You should use <pax.fx.init> for all FX functions!!!
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
*/

var pax = pax || {};

pax.fx = pax.fx || {};			// Fx methods
pax.fx.dict = {};				// Dictionary for tracking effect items
pax.fx.effectInterval = null;	// Interval for running effects

pax.css = pax.css || {};		// CSS methods

pax.fx.tween = {};				// FX Tweening lib
pax.fx.tween.util = {};			// FX Tweening utility lib

pax.fx.dragDict = {};			// Dictionary for tracking dragable elements
pax.fx.dropDict = {};			// Dictionary for tracking drop target elements


/*	Method: pax.fx.init
	Initialises an effect, and adds it to the efefcts dictionary.
	
	Parameters:
		target - Element ID of the DOM node we're to apply the effect to
		effect - which effect to use
		value - A value object the effect will use
		duration - Duration of the effect in milliseconds
		direction - Direction of effect, pass false to make it go backwards
		callBack - An optional callback method for when the effect has finished.
	
	Note:
		* Effects can be chained, by using the callBack method, or better yet, the pax.fx.chain method
		* Simultaneous effects can be run
		* See the individual effects for examples
*/
pax.fx.init = function( target, effect, value, duration, direction, callBack ) {
	var startTime = new Date().getTime();

	/*	
		WIP: for 'reveal' effect, we MUST have style.overflow = 'hidden', as such, we should be 
		able to capture style info first, set the overflow, and then reset the style, once the animation is finished

	*/
	
	value.startTime = startTime;
	value.duration = ( typeof( duration ) != 'undefined' ) ? duration : 1500;
	value.direction = ( typeof( direction ) != 'undefined' ) ? direction : true;
	value.callBack = callBack;

	return pax.fx.register( target, effect, value );
};


/*	Method: pax.fx.chain
	This method runs any number of effects, one after the other. Consecutive effects inherit prior effects parameters
	
	Parameters:
		fxList - List of effect init args to use

	Example:
		(start code)
			<input type="button" id="moveBox" value="Animate!">

			<div id='pax.fx.move.example1' style='position:absolute; overflow:hidden; background: #5555ff; 
				color: white; border: 3px solid #111; left: 4px; top: 60px; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example1'), false );
				var endPos = {x:startPos.x + 300, y:startPos.y + 150};
				var move = function() { 
					pax.fx.chain( [
						{	target: 'pax.fx.move.example1', 
							effect: 'move', 
							value: {startPos: startPos, endPos: endPos }, 
							duration: 2000,
							direction: true
						},
						{	effect: 'move', 
							value: {startPos: startPos, endPos: endPos },
							direction: false
						},
						{	effect: 'move', 
							value: {startPos: startPos, endPos: {x:200} },
							direction: true,
							duration: 500
						},
						{	effect: 'colourFade', 
							value: {startColour: '0000ff', endColour: 'ff0000'}
						},
						{	effect: 'opacityFade', 
							value: {startOpacity: 0, endOpacity: 100}, 
							duration: 1000,
							direction: false
						},
						{	effect: 'opacityFade', 
							value: {startOpacity: 0, endOpacity: 100}, 
							duration: 1000,
							direction: true
						},
						{	effect: 'move', 
							value: {startPos: {x:200}, endPos: startPos }, 
							duration: 2000,
							direction: true
						}
					] );
				};
				pax.$('moveBox').onclick = function() { move(); };
			:]
		(end)
		
*/
pax.fx.chain = function( fxList ) {
	var target, effect, value, duration, direction = null;

	//	Set initial effects parameters, so they can be inherited by each chained effect
	var fect = fxList[0];
	target = 	(typeof(fect.target) != 'undefined' )? 	fect.target: target;
	effect = 	(typeof(fect.effect) != 'undefined' )? 	fect.effect: effect;
	value = 	(typeof(fect.value) != 'undefined' )? 	fect.value: value;
	duration = 	(typeof(fect.duration) != 'undefined' )? 	fect.duration: duration;
	direction = (typeof(fect.direction) != 'undefined' )? fect.direction: direction;

	var fxFuncs = [];
	var callBackFunc = null;

	//	Loop backwards, assigning callback methods as you go.
	for( var i = fxList.length - 1; i > -1; i-- ) {
		var fect = fxList[i];
		//	Ok, we're inheriting in reverse order (duh), maybe create an array instead?
		/*
			. The array must inherit "forward"
			. We must inherit ALL values.
			. the inherited values should be used as (fxList.length - i)
		*/
		target = (typeof(fect.target) != 'undefined' )? fect.target: target;
		effect = (typeof(fect.effect) != 'undefined' )? fect.effect: effect;
		value = (typeof(fect.value) != 'undefined' )? fect.value: value;
		duration = (typeof(fect.duration) != 'undefined' )? fect.duration: duration;
		direction = (typeof(fect.direction) != 'undefined' )? fect.direction: direction;

		//	This function creates a call back function with the correct scope for the arguments
		var createCallBack = function( callBack ) {
			var ftarget = target;
			var feffect = effect;
			var fvalue = value;
			var fduration = duration;
			var fdirection = direction;
			
			if( typeof(callBack) == 'function' ) {
				callBackFunc = function() {
					pax.fx.init( ftarget, feffect, fvalue, fduration, fdirection, callBack );
				};
			} else {
				callBackFunc = function() {
					pax.fx.init( ftarget, feffect, fvalue, fduration, fdirection );
				};
			}
		};


		if( i == 0 ) {
			if( callBackFunc ) {
				pax.fx.init( target, effect, value, duration, direction, callBackFunc );
			} else {
				pax.fx.init( target, effect, value, duration, direction );
			}
		} else {
			if( callBackFunc ) {
				createCallBack( callBackFunc );
				/*
				var callBackFunc = function() {
					pax.fx.init( target, effect, value, duration, direction, callBackFunc );
				};
				*/
			} else {
				createCallBack();
			}
		}
	}
};

/*	Method: pax.fx.yoyo
	Initialises an effect, and makes it continiously go forwards and backwards.
	
	Parameters:
		target - element to perform the effect on
		effect - which effect to use
		value - object to pass to the effect
		duration - how long the effect goes for in milliseconds
		direction - which way the effect should start at, default = true = forwards, false = backwards.
		
	Returns:
		A function to stop the yoyo action by

	Example:
		(start code)
			<div id='pax.fx.move.example1' style='position:absolute; overflow:hidden; left: 4px; top: 60px;'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example1'), false );
				var endPos = {x:startPos.x, y:startPos.y + 250};
				var finishYoyo = pax.fx.yoyo( 'pax.fx.move.example1', 'move', {startPos: endPos, endPos: startPos }, 500 );
				exampleCleanup = function() {
					finishYoyo();
				};
			:]
		(end)
		You will note that the yoyo function returns an function to stop the action by

	Example:
		(start code)
			<input type="button" id="yoyoOpacity" value="opacityFade yoyo">
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );" value="opacityFade+">
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000, false );" value="opacityFade-">

			<div id='pax.fx.opacityFade.example1' style='overflow:hidden; background: #0000ff; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				function yoyoOpacity() {
					var finishYoyo = pax.fx.yoyo( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );
					exampleCleanup = function() {
						finishYoyo();
					};
				}
				pax.$('yoyoOpacity').onclick = function() { yoyoOpacity(); };
			:]
		(end)
		
	Example:
		(start code)
			<div id='pax.fx.move.example3' style='position:absolute; overflow:hidden; left: 4px; top: 60px;'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example3'), false );
				var endPos = {x:startPos.x, y:startPos.y + 250};
				var finishYoyo = pax.fx.yoyo( 'pax.fx.move.example3', 'move', {startPos: endPos, endPos: startPos }, 500 );
				var finishYoyo2 = pax.fx.yoyo( 'pax.fx.move.example3', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );
				exampleCleanup = function() {
					finishYoyo();
					finishYoyo2();
				};
			:]
		(end)
		You will note that the yoyo function returns an function to stop the action by

*/
pax.fx.yoyo = function( target, effect, value, duration, direction ) {
	pax.fx.init( target, effect, value, duration, !direction, function() {
		pax.fx.yoyo( target, effect, value, duration, !direction );
	} );

	//	We return a function to stop the effect with.
	return ( function() {
		if( typeof( pax.fx.dict[target][effect] ) != 'undefined' )pax.fx.dict[target][effect].stopYoyo = true;		
		pax.fx.finish( target, effect, pax.fx[effect].cleanup );
	} );

};

/*	Private Method: pax.fx.register
	Registers an ongoing effect with the fx manager
	
	Parameters:
		target - The unique ID of the DOM element the effect is being applied to
		effect - Which effect to run
		value - A value object for this effect, includes any variables
		
	Note:
		This method starts the effect interval, if it isn't running.
*/
pax.fx.register = function( target, effect, value ) {
	if( pax.util.hasKey( target, pax.fx.dict ) ) {
		//	Run finish, if an effect of this type is running, on the given target
		if( pax.util.hasKey( effect, pax.fx.dict[target] ) ) {
			pax.fx.finish( target, effect, pax.fx[effect].cleanup );
		}
	} else {
		pax.fx.dict[target] = {};
	}
	
	// Setup the timeout to stop the effect.
	var timeout = window.setTimeout( function() { pax.fx.finish( target, effect, pax.fx[effect].cleanup ); }, value.duration );
	value.timeout = timeout;
	
	pax.fx.dict[target][effect] = value;
	
	// Setup an interval, if we don't already have one. 16ms is approx 60fps
	if( ! pax.fx.effectInterval )pax.fx.effectInterval = setInterval( function() { pax.fx.animate(); }, 16 );
	
	return pax.fx.dict[target][effect];
};


/*	Method: pax.fx.cancelAll
	Cancels all ongoing effect on the given targets id
	
	Parameters:
		id - The unique ID of the DOM element the effect(s) are being applied to
		
*/
pax.fx.cancelAll = function( id ) {
	if( pax.fx.dict[id] ) {
		for( var effect in pax.fx.dict[id] ) {
			pax.fx.finish( id, effect, pax.fx[effect].cleanup );
		}
	}
};


/*
	Private method: pax.fx.animate - Iterates through the effects dict, and runs each effect.
*/
pax.fx.animate = function() {
	for( var target in pax.fx.dict ) {
		for( var effect in pax.fx.dict[target] ) {
			if( pax.util.hasKey( effect, pax.fx ) ) {
				
				var ele = document.getElementById( target );
				var val = pax.fx.dict[target][effect];
				
				// Calculate as a percentage where we're up to, using start time and duration.
				var pct = ( ( ( (new Date()).getTime() ) - val.startTime ) / val.duration ) * 100;
				pct = ( pct > 100 )? 100 : pct;
				pct = ( pct < 0 )? 0 : pct;
				
				pax.fx[effect]( val, ele, pct );

			} else {
				//	We're trying to run an undefined effect!
				console.log( 'pax.fx: effect [' + effect + '] not found' );
			}
		}
	}
};


/*	Private Method: pax.fx.finish
	Finishes an effect, by running cleanup, and optional callback.
	
*/
pax.fx.finish = function ( target, effect, cleanup, callBack ) {
    var val = pax.fx.dict[target][effect];
    var ele = document.getElementById( target );
   
	//    Run the effects cleanup method
	if( cleanup )cleanup( val, ele );
   
    if( pax.util.hasKey( target, pax.fx.dict ) ) {
        if( val )if( typeof( val.timeout ) != 'undefined' )clearTimeout( val.timeout );
        if( pax.util.hasKey( effect, pax.fx.dict[target] ) )delete pax.fx.dict[target][effect];
    }
   
    //    If the dict is empty, clear the interval
    if( pax.fx.dict == {} ) {
        clearInterval( pax.fx.effectInterval );
        pax.fx.effectInterval = null;
    }

    if( val ) {
        if( !val.stopYoyo ) {
            if( typeof( val.callBack ) == 'function' )val.callBack();    //    Run the initial callback
        }
    }
   
    if( typeof( callBack ) == 'function' )callBack();            //    Run the specified callback   
};


/*	Method: pax.css.opacity
	Sets the opacity of an element
	
	Parameters:
		element - DOM pointer to the element we want to set the opacity of
		percent - percentage of opacity
		
	Example:
		(start code)
			<div id='pax.css.opacity.example1' style='background:#2233DD; border: 2px solid red'>This is a test</div>
			[:.
				pax.css.opacity( pax.$('pax.css.opacity.example1'), 50 );
			:]
		(end)
		Sets the opacity of an element.
*/
pax.css.opacity = function( element, percent ) {
	if( ! element )return;
	if( ! element.style ) {
		element.setAttribute( 'style', {} );
		element.style = {};
	}
	element.style.filter = "alpha(opacity=" + percent + ")";
	element.style.MozOpacity = percent / 100;
	element.style.opacity = percent / 100;
	element.style.KhtmlOpacity = percent / 100;
	
	//	Workaround for IE - we MUST have layout for this to work
	if( pax.isIe && ! element.currentStyle.hasLayout )element.style.zoom = 1;
};



/*	Private Method: pax.fx.tween.sinePercent
	Returns an approximate sine percent value of pi / 180
	
	Parameters:
		percent - percentage that we want the sine function to use.
*/
pax.fx.tween.sinePercent = function( percent ) {
	return Math.sin( percent * 0.016 );	
};

/*	Private Method: tween.position
	Function to calculate x, y, width and height at a certain percentage.
	
	Parameters:
		start - starting position object, eg {x:100, y:50}
		end - ending position object, eg {x:200, y:150}
		percent - how far we have moved percentage-wise
		tweenMethod - method to use for tweening.
*/
pax.fx.tween.position = function( start, end, percent, tweenMethod ) {
	var result = {};
	if( typeof( tweenMethod ) == 'function' ) {
		result.x = Math.floor( start.x + (end.x - start.x) * tweenMethod( (percent) ) + 0.5);
		result.y = Math.floor( start.y + (end.y - start.y) * tweenMethod( (percent) ) + 0.5);
		result.width = Math.floor( start.width + (end.width - start.width) * tweenMethod( (percent) ) + 0.5);
		result.height = Math.floor( start.height + (end.height - start.height) * tweenMethod( (percent) ) + 0.5);
	} else {
		result.x = Math.floor( start.x + (end.x - start.x) * (percent / 100) + 0.5);
		result.y = Math.floor( start.y + (end.y - start.y) * (percent / 100) + 0.5);
		result.width = Math.floor( start.width + (end.width - start.width) * (percent / 100) + 0.5);
		result.height = Math.floor( start.height + (end.height - start.height) * (percent / 100) + 0.5);
	}
	return result;
};

/*	Private Method: pax.fx.tween.util.dec2hex
	Calculate the hexidecimal value of a decimal
*/
pax.fx.tween.util.dec2hex = function( dec ) {
	var hexDigit = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
	return hexDigit[dec>>4] + hexDigit[dec&15];
};

/*	Private Method: pax.fx.tween.util.hex2dec
	Calculate the decimal value of a hexidecimal
*/
pax.fx.tween.util.hex2dec = function( hex ) {
	return parseInt(hex,16);
};

/*	Private Method: pax.fx.tween.getColour
	Calculate the colour value between start and end, at percentage pct.
	
	Parameters:
		start - starting colour without the hash, eg: FF7EA8.
		end - ending colour without the hash, eg: 8AE7FF.
		pct - how far through we are, in percent
		
	Returns:
		The new colour, including the hash
*/
pax.fx.tween.getColour = function( start, end, pct ) {
	pct = pct / 100;
	
	var r1 = pax.fx.tween.util.hex2dec( start.slice(0, 2) );
	var g1 = pax.fx.tween.util.hex2dec( start.slice(2, 4) );
	var b1 = pax.fx.tween.util.hex2dec( start.slice(4, 6) );
	
	var r2 = pax.fx.tween.util.hex2dec( end.slice(0, 2) );
	var g2 = pax.fx.tween.util.hex2dec( end.slice(2, 4) );
	var b2 = pax.fx.tween.util.hex2dec( end.slice(4, 6) );

	var r = Math.floor( r1 + (pct * (r2 - r1) ) + .5 );
	var g = Math.floor( g1 + (pct * (g2 - g1) ) + .5 );
	var b = Math.floor( b1 + (pct * (b2 - b1) ) + .5 );

	return "#" + pax.fx.tween.util.dec2hex(r) + pax.fx.tween.util.dec2hex(g) + pax.fx.tween.util.dec2hex(b);
};


/*	Method: pax.fx.reveal
	To use this method, initialise via <pax.fx.init>. Reveals an element to a specified height, see <pax.fx.init> for details on how to use this.
	
	Parameters:
		endSize - Final height of element (start is assumed to be 0 and style.display='none')

	WIP:
		* Add ability to reveal by width as well

	Example:
		(start code)
			<input type="button" onclick="pax.fx.init( 'pax.fx.reveal.example1', 'reveal', {endSize: 160}, 1000 );" value="reveal+">
			<input type="button" onclick="pax.fx.init( 'pax.fx.reveal.example1', 'reveal', {endSize: 160}, 1000, false );" value="reveal-">

			<span id='pax.fx.reveal.example1' style='display:none; overflow:hidden; background: #0000ff; width: 200px;border-bottom:1px solid #000'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</span>
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.
*/
pax.fx.reveal = function( val, ele, pct ) {
	if( val.direction ) {
		ele.style.height = ( val.endSize * pax.fx.tween.sinePercent( pct ) ) + 'px';
	} else {
		ele.style.height = ( val.endSize * pax.fx.tween.sinePercent( 100 - pct ) ) + 'px';
	}
	ele.style.display = 'block';
};

/*
	Private method: pax.fx.reveal.cleanup - Cleans up the reveal, ie sets the final height.
*/
pax.fx.reveal.cleanup = function( val, ele ) {
	ele.style.height = ( val.direction )? val.endSize + 'px' : '1px';
	if( !val.direction )ele.style.display = 'none';
};


/*	Method: pax.fx.colourFade
	To use this method, initialise via <pax.fx.init>. Fades between given background colours.
	
	Parameters:
		startColour - Colour to start the fade at
		endColour - Colour to finish the fade at

	Example:
		(start code)
			<input type="button" onclick="pax.fx.init( 'pax.fx.colourFade.example1', 'colourFade', {startColour: '0000ff', endColour: 'ff0000'}, 1000 );" value="ColourFade+">
			<input type="button" onclick="pax.fx.init( 'pax.fx.colourFade.example1', 'colourFade', {startColour: '0000ff', endColour: 'ff0000'}, 1000, false );" value="ColourFade-">

			<div id='pax.fx.colourFade.example1' style='overflow:hidden; background: #0000ff; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.
*/
pax.fx.colourFade = function ( val, ele, pct ) {
	if( val.direction ) {
		ele.style.background = pax.fx.tween.getColour( val.startColour, val.endColour, pct );
	} else {
		ele.style.background = pax.fx.tween.getColour( val.startColour, val.endColour, (100 - pct) );
	}
};

/*
	Private method: fx.colourFade.cleanup - Cleans up the colour fade, ie sets the final colour
*/
pax.fx.colourFade.cleanup = function ( val, ele ) {
	ele.style.background = ( val.direction )? val.endColour : val.startColour;
};


/*	Method: pax.fx.opacityFade
	To use this method, initialise via <pax.fx.init>. Fades between opacity levels
	
	Parameters:
		startOpacity - Opacity to start the fade at
		endOpacity - Opacity to finish the fade at

	Example:
		(start code)
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000, false );" value="Fade out">
			<input type="button" onclick="pax.fx.init( 'pax.fx.opacityFade.example1', 'opacityFade', {startOpacity: 0, endOpacity: 100}, 1000 );" value="Fade in">

			<div id='pax.fx.opacityFade.example1' style='overflow:hidden; background: #0000ff; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.
		
*/
pax.fx.opacityFade = function ( val, ele, pct ) {
    //    Calculate the percentage here, and set accordingly.
    var opacityDiff = val.endOpacity - val.startOpacity;
    if( val.direction )pax.css.opacity( ele, ( val.startOpacity + ( (pct / 100) * opacityDiff ) ) );
    else pax.css.opacity( ele, ( val.endOpacity - ( (pct / 100) * opacityDiff ) ) );
};


/*
	Private method: fx.opacityFade.cleanup - Cleans up the opacity fade, ie sets the final opacity
*/
pax.fx.opacityFade.cleanup = function ( val, ele ) {
	if( val.direction )pax.css.opacity( ele, val.endOpacity );
	else pax.css.opacity( ele, val.startOpacity );
};


/*	Method: pax.fx.fadeIn
	Fades in an element in optional time miliseconds

	Parameters:
		ele - element to fade in
		time - time in milliseconds to fade for, default time is 1000ms
		
	Example:
		(start code)
			<div id='pax.fx.fadeIn.example1' style='MozOpacity:0;opacity:0;KhtmlOpacity:0;filter:alpha(opacity=0);'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				pax.fx.fadeIn( 'pax.fx.fadeIn.example1' );
			:]
		(end)
		
*/
pax.fx.fadeIn = function( ele, time, callBack ) {
	time = (typeof(time) != 'undefined' )? time: 1000;
	pax.fx.init( ele, 'opacityFade', {startOpacity: 0, endOpacity: 100}, time, true, callBack );
};


/*	Method: pax.fx.fadeOut
	Fades out an element in optional time miliseconds

	Parameters:
		ele - element to fade out
		time - time in milliseconds to fade for, default time is 1000ms
		
	Example:
		(start code)
			<div id='pax.fx.fadeOut.example1'>
				<img border=0 src='/pax/documentation/basketball.gif'>
			</div>
			[:.
				pax.fx.fadeOut( 'pax.fx.fadeOut.example1' );
			:]
		(end)
		
*/
pax.fx.fadeOut = function( ele, time, callBack ) {
	time = (typeof(time) != 'undefined' )? time: 1000;
	pax.fx.init( ele, 'opacityFade', { startOpacity: 0, endOpacity: 100 }, time, false, callBack );
};


/*	Method: pax.fx.move
	To use this method, initialise via <pax.fx.init>. Moves an element from one spot to another
	
	Parameters:
		startPos - Object with {x, y} co-ords to start at
		endPos - Object with {x, y} co-ords to end at

	Example:
		(start code)
			<input type="button" id="moveDown" value="Move down">
			<input type="button" id="moveUp" value="Move up">
			<input type="button" id="random" value="Random">

			<div id='pax.fx.move.example1' style='position:absolute; overflow:hidden; background: #5555ff; color: white; border: 3px solid #111; left: 4px; top: 60px; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				var startPos = pax.util.getPosition( pax.$('pax.fx.move.example1'), false );
				var endPos = {x:startPos.x + 300, y:startPos.y + 150};
				var move = function( direction ) {
					pax.fx.init( 'pax.fx.move.example1', 'move', {startPos: startPos, endPos: endPos }, 1000, direction );
				};
				pax.$('moveDown').onclick = function() { move( true ); };
				pax.$('moveUp').onclick = function() { move( false ); };
				
				var random = function() {
					var endPos = {
						x: startPos.x + parseInt( Math.random() * 200),
						y: startPos.y + parseInt( Math.random() * 200)
					};
					var direction = (Math.random() > 0.5);
					pax.fx.init( 'pax.fx.move.example1', 'move', {startPos: startPos, endPos: endPos }, 1000, direction );
				};
				pax.$('random').onclick = function() { random(); };
			:]
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing, and it .
*/
pax.fx.move = function ( val, ele, pct ) {
	ele.style.position = 'absolute';
	if( val.direction ) {
		var pos = pax.fx.tween.position( val.startPos, val.endPos, pct, pax.fx.tween.sinePercent );
		if( !isNaN( pos.x ) )ele.style.left = pos.x + 'px';
		if( !isNaN( pos.y ) )ele.style.top = pos.y + 'px';
	} else {
		var pos = pax.fx.tween.position( val.startPos, val.endPos, (100 - pct), pax.fx.tween.sinePercent );
		if( !isNaN( pos.x ) )ele.style.left = pos.x + 'px';
		if( !isNaN( pos.y ) )ele.style.top = pos.y + 'px';
	}
};

/*	
	Private method: pax.fx.move.cleanup - Sets the final position
*/
pax.fx.move.cleanup = function ( val, ele ) {
	if( val.direction ) {
		if( typeof( val.endPos.x ) != 'undefined' )ele.style.left = val.endPos.x + 'px';
		if( typeof( val.endPos.y ) != 'undefined' )ele.style.top = val.endPos.y + 'px';
	} else {
		if( typeof( val.startPos.x ) != 'undefined' )ele.style.left = val.startPos.x + 'px';
		if( typeof( val.startPos.y ) != 'undefined' )ele.style.top = val.startPos.y + 'px';
	}
};


/*	Method: pax.fx.size
	To use this method, initialise via <pax.fx.init>. Sizes an element from one size to another
	
	Parameters:
		startSize - Object with {width, height} parameters to start at
		endSize - Object with {width, height} parameters to end at

	Example:
		(start code)
			<input type="button" id="sizeUp" value="size up">
			<input type="button" id="sizeDown" value="size down">
			<input type="button" id="random" value="Random">

			<div id='pax.fx.size.example1' style='position:absolute; overflow:hidden; background: #5555ff; color: white; border: 3px solid #111; left: 4px; top: 60px; width:200px; height: 160px'>
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
				upsim lorum dorum bordum upsim lorum dorum bordum upsim lorum dorum bordum
			</div>
			[:.
				var startSize = pax.util.getPosition( pax.$('pax.fx.size.example1'), false );
				var endSize = {width:startSize.width + 300, height:startSize.height + 150};
				var size = function( direction ) {
					pax.fx.init( 'pax.fx.size.example1', 'size', {startSize: startSize, endSize: endSize }, 1000, direction );
				};
				pax.$('sizeDown').onclick = function() { size( false ); };
				pax.$('sizeUp').onclick = function() { size( true ); };
				
				var random = function() {
					var endSize = {
						width: startSize.width + parseInt( Math.random() * 200),
						height: startSize.height + parseInt( Math.random() * 200)
					};
					var direction = (Math.random() > 0.5);
					pax.fx.init( 'pax.fx.size.example1', 'size', {startSize: startSize, endSize: endSize }, 1000, direction );
				};
				pax.$('random').onclick = function() { random(); };
			:]
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing, and it .
*/
pax.fx.size = function ( val, ele, pct ) {
	if( val.direction ) {
		var size = pax.fx.tween.position( val.startSize, val.endSize, pct, pax.fx.tween.sinePercent );
		ele.style.width = size.width + 'px';
		ele.style.height = size.height + 'px';
	} else {
		var size = pax.fx.tween.position( val.startSize, val.endSize, (100 - pct), pax.fx.tween.sinePercent );
		ele.style.width = size.width + 'px';
		ele.style.height = size.height + 'px';
	}
};

/*
	Private method: pax.fx.size.cleanup - Sets the final size
*/
pax.fx.size.cleanup = function ( val, ele ) {
	if( val.direction ) {
		ele.style.width = val.endSize.width + 'px';
		ele.style.height = val.endSize.height + 'px';
	} else {
		ele.style.width = val.startSize.width + 'px';
		ele.style.height = val.startSize.height + 'px';
	}
};


/*	Method: pax.fx.cursorPos
	Get co-ordinates of the mouse cursor
	
	Paramaters:
		event - the event to find the cursor position with.
		
	Returns:
		an object with the x, y co-ordinates of the cursor

	WIP:
		* Perhaps move to util class

*/
pax.fx.cursorPos = function( event ) {
	var x = ( typeof( window.scrollX ) != 'undefined' )? 
		event.clientX + window.scrollX : 
		window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
		
	var y = ( typeof( window.scrollY ) != 'undefined' )? 
		event.clientY + window.scrollY : 
		window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
	
	return { x: x, y: y };
};


/*	Method: pax.fx.drag
	Sets an element as draggable
	
	Paramaters:
		event - the event to find the cursor position of.
		args - optional argument object to pass to the drag function, ie: {}
		* constrain - either 'vertical' or 'horizontal', eg: {constrain: 'vertical'}
		* grid - integer grid value, 0 = no grid, eg: {grid: 10}
		* box - limit where element can be dragged, can be the object returned by <pax.util.getPosition>, eg: {x:10, y:20, width:70, height:70} or co-ordinates, eg: {x:10, y:20, x2:80, y2:90}
		* snap - this will "snap" to a given "line".
		* callStart - function to be called when a draggable object starts being dragged, paramater is an object {x,y,args}
		* callMove - function to be called when a draggable object is being dragged, paramater is an object {x,y,args}, be careful as it's continiously being called
		* callBack - function to be called when a draggable object is dropped, paramater is an object {x,y,args}
	
	Example:
		(start code)
			<span id="pax.fx.drag.vline" style="position:absolute;left:550px;width:0px;height:400px;border-left:1px solid #aaa;"></span>
			<span id="pax.fx.drag.hline" style="position:absolute;left:525px;top:300px;width:400px;height:1px;border-top:1px solid #aaa;"></span>
			<span id="pax.fx.drag.example7.box" style="position:absolute;left:200px;top:125px;width:300px;height:200px;border: 1px solid #000;"></span>
			
			<div id="pax.fx.drag.example1" style="position:absolute;left:10px;top:40px;width:12em;border:1px solid #aaa; background:#ccf;padding:8px">
				Drag this box anywhere
			</div>
			
			<div id="pax.fx.drag.example2" style="position:absolute;left:10px;top:100px;width:12em;border:1px solid #aaa; background:#cfc;padding:8px">
				Drag this box left and right
			</div>
			
			<div id="pax.fx.drag.example3" style="position:absolute;left:10px;top:160px;width:12em;border:1px solid #aaa; background:#fcc;padding:8px">
				Drag this box up and down
			</div>
			
			<div id="pax.fx.drag.example4" style="position:absolute;left:10px;top:220px;width:12em;border:1px solid #aaa; background:#cff;padding:8px">
				Drag this box on a grid
			</div>
			
			<div id="pax.fx.drag.example5" style="position:absolute;left:10px;top:280px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Drag this box within the window only
			</div>
			
			<div id="pax.fx.drag.example6" style="position:absolute;left:200px;top:40px;border:1px solid #aaa; background:#ffc;padding:8px">
				<div id="pax.fx.drag.example6.handle" style="width:12em;background:#99f;padding:4px" >Drag Handle</div>
				<div class="content" style="width:12em;">
					Drag this box using the handle.
				</div>
			</div>
			
			<div id="pax.fx.drag.example7" style="position:absolute;left:200px;top:125px;width:12em;border:1px solid #aaa; background:#f0f;padding:8px">
				Drag this box within a certain square only
			</div>
			
			<div id="pax.fx.drag.example8" style="position:absolute;left:10px;top:340px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Drag this box and That box
			</div>
			
			<div id="pax.fx.drag.example9" style="position:absolute;left:200px;top:340px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				That box
			</div>
			
			<div id="pax.fx.drag.example10" style="position:absolute;left:550px;top:40px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Snap box - drag near lines
			</div>

			<div id="pax.fx.drag.example11" style="position:absolute;left:550px;top:100px;width:12em;border:1px solid #aaa; background:#fcf;padding:8px">
				Return box - drag and drop to return
			</div>

			[:.
				pax.fx.drag( pax.$('pax.fx.drag.example1') );
				pax.fx.drag( pax.$('pax.fx.drag.example2'), { constrain: 'horizontal' } );
				pax.fx.drag( pax.$('pax.fx.drag.example3'), { constrain: 'vertical' } );
				pax.fx.drag( pax.$('pax.fx.drag.example4'), { grid: 30, callStart: function(arg) { console.log( arg.x + ' : ' + arg.y + ' : ' + arg.args ); } } );
				pax.fx.drag( pax.$('pax.fx.drag.example5'), { box: pax.util.getPosition( pax.$('exampleDiv'), false ) } );
				pax.fx.drag( pax.$('pax.fx.drag.example6'), { handle: pax.$('pax.fx.drag.example6.handle') } );
				pax.fx.drag( pax.$('pax.fx.drag.example7'), { box: {x:200, y:125, x2:500, y2:325} } );
				pax.fx.drag( pax.$('pax.fx.drag.example8') );
				pax.fx.drag( pax.$('pax.fx.drag.example9'), { handle: pax.$('pax.fx.drag.example8') } );
				pax.fx.drag( pax.$('pax.fx.drag.example9') );
				pax.fx.drag( pax.$('pax.fx.drag.example10'), { snap: [{x:550,y:50}, {y:300,limit:20}] } );				
				pax.fx.drag( pax.$('pax.fx.drag.example11'), { return: true } );				
			:]
		(end)
		Note that we cater for almost all situations here, and you can obviously combine each argument type.

*/
pax.fx.drag = function( element, args ) {
	element = ( typeof( element ) == 'string' )? document.getElementById( element ) : element;
	var boundElement = null;
	if( typeof( args ) != 'undefined' ) {
		var handle = (typeof(args.handle) != 'undefined' )? args.handle: null;
		args.element = element;
		if( handle ) {
			boundElement = pax.event.bind( handle, 'mousedown', function( event ) { 
				if( pax.util.getType( args.onBeforeDrag ) == 'function' )args.onBeforeDrag( event, args );
				pax.fx.dragElement( event, args ); }
			);
		} else {
			boundElement = pax.event.bind( element, 'mousedown', function( event ) { 
				if( pax.util.getType( args.onBeforeDrag ) == 'function' )args.onBeforeDrag( event, args );
				pax.fx.dragElement( event, args ); }
			);
		}
	} else {
		boundElement = pax.event.bind( element, 'mousedown', pax.fx.dragElement  );
	}
	
	return boundElement;
};


				
/*	Private Method: pax.fx.dragElement
	set drag events on elements, see the 
	
*/
pax.fx.dragElement = function( event, args ) {
	var eventElement = event.target || window.event.srcElement;
	var ele = eventElement;

	//	Ensure we have a valid args object, even if it is empty, and assign the target element if set
	if( typeof(args) != 'undefined' ) {
		ele = (typeof(args.element) != 'undefined')? args.element: ele;
	} else {
		args = {};
	}
	
	if( ele.nodeType == 3 )ele = ele.parentNode;		//	If this is a text node, use its parent element.

	//	Set the configuration drag object
	var cur = pax.fx.cursorPos( event );
	var dObj = {
		element: ele,
		zIndex: 1000,
		startZIndex: ( isNaN( parseInt(ele.style.zIndex) ) )? 0 : parseInt( ele.style.zIndex ),
		startCursorX: cur.x,
		startCursorY: cur.y,
		startLeft: ( isNaN(parseInt(ele.style.left )) )? 0: parseInt(ele.style.left ),
		startTop: ( isNaN(parseInt(ele.style.top )) )? 0: parseInt(ele.style.top ),
		args: args
	};

	ele.style.zIndex = dObj.zIndex;

	//	Function to move the objects with
	var dragMove = function( event ) {
		var cur = pax.fx.cursorPos( event );

		//	Loop through the objects we're dragging
		for( var dList in pax.fx.dragDict ) {
			var dragList = pax.fx.dragDict[dList];
			for( var d in dragList ) {
				var dObj = dragList[d];

				dObj.element.style.position = 'absolute';

				var arg = { constrain: '', grid: 0, box: {}, snap: [] };

				if( typeof(dObj.args) != 'undefined' ) {
					for( var a in args ) {
						arg[a] = args[a];
					}
				}
				
				var newX = dObj.startLeft + cur.x - dObj.startCursorX;
				var newY = dObj.startTop + cur.y - dObj.startCursorY;

				//	Snap to grid
				if( arg.grid > 0 ) {
					var offX = newX % arg.grid;
					newX += (offX >= arg.grid / 2)? (arg.grid - offX): -offX;
					var offY = newY % arg.grid;
					newY += (offY >= arg.grid / 2)? (arg.grid - offY): -offY;
				}

				//	Check we're inside the box area
				if( arg.box != {} ) {
					var dims = pax.util.getPosition( dObj.element, false );
					
					if( typeof(arg.box.x) != 'undefined' ) {
						if( newX < arg.box.x )newX = arg.box.x;
					}
					
					if( typeof(arg.box.y) != 'undefined' ) {
						if( newY < arg.box.y )newY = arg.box.y;
					}
					
					if( typeof(arg.box.x2) != 'undefined' ) {
						if( newX > (arg.box.x2 - dims.width) )newX = (arg.box.x2 - dims.width);
					} else if( typeof(arg.box.width) != 'undefined' ) {
						if( newX > (arg.box.width + arg.box.x - dims.width) )newX = (arg.box.width + arg.box.x - dims.width);
					}
					
					if( typeof(arg.box.y2) != 'undefined' ) {
						if( newY > (arg.box.y2 - dims.height) )newY = (arg.box.y2 - dims.height);
					} else if( typeof(arg.box.height) != 'undefined' ) {
						if( newY > (arg.box.height + arg.box.y - dims.height) )newY = (arg.box.height + arg.box.y - dims.height);
					}
				}

				//	Check if we're close to a snap area, and snap accordingly
				if( arg.snap != [] ) {
					var dims = pax.util.getPosition( dObj.element, false );
					
					for( var s in arg.snap ) {
						var snap = arg.snap[s];
						var limit = (typeof(snap.limit)!='undefined')? snap.limit: 10;
						
						if( typeof(snap.x) != 'undefined' ) {
							if( ( newX <= (snap.x + limit) ) && ( newX >= (snap.x - limit) ) ) {
								newX = snap.x;
							}
						}
						
						if( typeof(snap.y) != 'undefined' ) {
							if( ( newY <= (snap.y + limit) ) && ( newY >= (snap.y - limit) ) ) {
								newY = snap.y;
							}
						}						
					}					
				}

				//	Apply constraints and set new co-ordinates
				if( arg.constrain != 'vertical' ) {
					dObj.element.style.left = newX + 'px';
				}
				if( arg.constrain != 'horizontal' ) {
					dObj.element.style.top = newY + 'px';
				}
				
				//	Look for drop target observers
				for( var dropl in pax.fx.dropDict ) {
					var dropList = pax.fx.dropDict[dropl];
					for( var dr in dropList ) {
						var dropObj = dropList[dr];
						//	Check if the dragged object is on top of the drop target
						var dropDims = pax.util.getPosition( dropObj.element );
						
						//	Note: we're going to assume we want to fire on the mouse co-ords, NOT the object.
						if( cur.x >= dropDims.x && cur.x <= dropDims.x + dropDims.width && cur.y >= dropDims.y && cur.y <= dropDims.y + dropDims.height ) {

							//	Notify the observer of hover position
							if( typeof(dropObj.hover) == 'function' )dropObj.hover( cur.x, cur.y, dObj.args );
							
							if( ! dropObj.hoverOver ) {
								// Notify the observer - may have to use x and y relative to observer obj.
								if( typeof(dropObj.hoverEnter) == 'function' )dropObj.hoverEnter( cur.x, cur.y, dObj.args );
							}
							dropObj.hoverOver = true;
						} else {
							if( dropObj.hoverOver ) {
								//	Notify hover off
								if( typeof(dropObj.hoverExit) == 'function' )dropObj.hoverExit( cur.x, cur.y, dObj.args );
							}
							dropObj.hoverOver = false;
						}
					}
				}
				
				//	Run the callMove function every 10 ms
				var now = new Date().getTime();
				if( (dObj.callMoveTimer == null || ( dObj.callMoveTimer < (now - 10) ) ) && typeof(dObj.args.callMove) == 'function' ) {
					dObj.args.callMove( {x:newX, y:newY, args:dObj.args} );
				}
				dObj.callMoveTimer = now;
			}
		}
		
		//	Prevent text selection
		if( event.preventDefault ) {
			event.preventDefault();
		} else {
			window.event.cancelBubble = true;
			window.event.returnValue = false;
		}
	};

	//	Function that fires when the dragging is finished, cleans up the drag objects
	var dragStop = function( event ) {
		var ele = event.target || window.event.srcElement;
		
		for( var dList in pax.fx.dragDict ) {
			var dragList = pax.fx.dragDict[dList];
			for( var d in dragList ) {
				//	Reset the zIndex of the object.
				var dObj = dragList[d];
				
				var resetZIndex = ( typeof(dObj.args.resetZIndex) != 'undefined' )? dObj.args.resetZIndex: true;
				if( resetZIndex ) {
					dObj.element.style.zIndex = dObj.startZIndex;					
				}
				
				//	Look for drop targets
				for( var dropl in pax.fx.dropDict ) {
					var dropList = pax.fx.dropDict[dropl];
					for( var dr in dropList ) {
						var dropObj = dropList[dr];
						//	Check if the dragged object is on top of the drop target
						var dropDims = pax.util.getPosition( dropObj.element );
						
						//	Note: we're going to assume we want to fire on the mouse co-ords, NOT the object.
						var cur = pax.fx.cursorPos( event );
						
						if( cur.x >= dropDims.x && cur.x <= dropDims.x + dropDims.width && cur.y >= dropDims.y && cur.y <= dropDims.y + dropDims.height ) {
							// Notify the observer - may have to use x and y relative to observer obj.
							if( typeof(dropObj.finish) == 'function' )dropObj.finish( cur.x, cur.y, dObj.args );
						}
					}
				}
				
				//	Could do the "snap-back" animation here, in case it's an 'invalid target...
				//	Run the callback
				if( typeof(dObj.args.callBack) == 'function' )dObj.args.callBack( {x:parseInt(dObj.element.style.left), y:parseInt(dObj.element.style.top), args:dObj.args} );
			}
			//	Remove the object's references
			delete dragList;
		}
		
		//	Unbind the events
		pax.event.unbind( document, 'mousemove', dragMove );
		pax.event.unbind( document, 'mouseup', dragStop );
		
		// Clear the dict
		pax.fx.dragDict = {};
		
		if( pax.util.getType( args.onAfterDrag ) == 'function' )args.onAfterDrag( event, dObj.args );
	};

	//	Capture events
	pax.event.bind( document, 'mousemove', dragMove );
	pax.event.bind( document, 'mouseup', dragStop );

	//	Prevent text selection
	if( event.preventDefault ) {
		event.preventDefault();
	} else {
		window.event.cancelBubble = true;
		window.event.returnValue = false;
	}
	
	if( typeof( pax.fx.dragDict[eventElement] ) == 'undefined' )pax.fx.dragDict[eventElement] = [];
	pax.fx.dragDict[eventElement].push( dObj );

	//	Run the callStart function
	if( typeof(dObj.args.callStart) == 'function' )dObj.args.callStart( {x:parseInt(dObj.element.style.left), y:parseInt(dObj.element.style.top), args:dObj.args} );
};


/*	Method: pax.fx.drop
	Sets an element as a drop target for a draggable element
	
	Paramaters:
		element - what DOM element to use as a drop target
		args - an object with various methods and options
		* finish - function that fires when the drop finishes on the target. Parameters are {x, y, args}
		* hover - function that fires when the target is hovered by a drag element. Parameters are {x, y, args}
		* hoverEnter - function that fires when a drag element hovers above the drop target. Parameters are {x, y, args}
		* hoverExit - function that fires when a drag element exits the drop target. Parameters are {x, y, args}
		
		The args parameter is that of the drag element.
	
	Example:
		(start code)
			<span id="pax.fx.drop.example1.target" style="position:absolute;left:200px;top:125px;width:300px;height:200px;border: 1px solid #000;">
				Drop target
			</span>
			
			<div id="pax.fx.drag.example1" style="position:absolute;left:10px;top:40px;width:12em;border:1px solid #aaa; background:#ccf;padding:8px">
				Drag this box onto the drop target
			</div>
			
			[:.
				pax.fx.drag( pax.$('pax.fx.drag.example1') );
				var args = {
					finish: function( x, y, args ) {console.log('finish ' + x + ' : ' + y);},
					hover: function( x, y, args ) {return;console.log('hover ' + x + ' : ' + y);},
					hoverEnter: function( x, y, args ) {pax.$('pax.fx.drop.example1.target').style.border='3px solid red';console.log('hoverEnter ' + x + ' : ' + y);},
					hoverExit: function( x, y, args ) {pax.$('pax.fx.drop.example1.target').style.border='1px solid #000';console.log('hoverExit ' + x + ' : ' + y);}
				};
				pax.fx.drop( pax.$('pax.fx.drop.example1.target'), args );
				exampleCleanup = function() {
					pax.fx.dropDict = {};
				};
			:]
		(end)
		Note that the example uses the <pax.fx.init> method, which all effects should be doing.

*/
pax.fx.drop = function( element, args ) {
	var dObj = {
		element: element,
		hoverOver: false
	};

	if( typeof(args) != 'undefined' ) {
		dObj.finish = (typeof(args.finish) == 'function')? args.finish: null;
		dObj.hover = (typeof(args.hover) == 'function')? args.hover: null;
		dObj.hoverEnter = (typeof(args.hoverEnter) == 'function')? args.hoverEnter: null;
		dObj.hoverExit = (typeof(args.hoverExit) == 'function')? args.hoverExit: null;
	}
	
	if( typeof( pax.fx.dropDict[element] ) == 'undefined' )pax.fx.dropDict[element] = [];
	pax.fx.dropDict[element].push( dObj );	
};